#########################################################################################
# Package       Wx::Perl::SimpleThread
# Description:  Simple thread handling for Wx
# Created       Mon Mar 29 14:18:47 2010
# SVN Id        $Id: SimpleThread.pm 5 2010-03-30 09:55:07Z mark.dootson $
# Copyright:    Copyright (c) 2010 Mark Dootson
# Licence:      This program is free software; you can redistribute it 
#               and/or modify it under the same terms as Perl itself
#########################################################################################

=head1 NAME

Wx::Perl::SimpleThread - simple thread model for Wx

=head1 VERSION

Version 0.02

=head1 AUTHOR

Mark Dootson <mdootson@cpan.org>

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut

#########################################################################################

package Wx::Perl::SimpleThread;

#########################################################################################

use strict;
use warnings;
use 5.008;
require Exporter;
use base qw( Exporter );
use threads;
use threads::shared;
use Thread::Queue;
use UNIVERSAL::require;
use threads 'exit' => 'threads_only';
use Time::HiRes qw ( sleep );

#--------------------------------------------
# Queues
#--------------------------------------------
my $_wxpst_createqueue    = Thread::Queue->new;
my $_wxpst_requestqueue   = Thread::Queue->new;
my $_wxpst_initqueue      = Thread::Queue->new;
#---------------------------------------------
# Handler Thread
#---------------------------------------------
my $_wxpst_threadhandler = threads->create( \&_threadhandler );
#---------------------------------------------
# Main Thread
#---------------------------------------------
our $VERSION = '0.02';

my  $_threadrequests = 0;
our $_wxpst_timer;

sub _remove_thread_handler { $_wxpst_timer->_remove_handler( shift ); }
sub _add_thread_handler { $_wxpst_timer->_add_handler( shift ); }

{
    #---------------------------------------------------------------------
    # initialise timer
    require Wx::Perl::SimpleThread::Timer;
    $_wxpst_timer = Wx::Perl::SimpleThread::Timer->new();
    $_wxpst_timer->Start(100);
    #---------------------------------------------------------------------
}

sub _threadhandler {
    my $handlercontinue = 1;
    $SIG{'KILL'} = sub { threads->exit; };
    $SIG{'TERM'} = sub { $handlercontinue = 0;};
    while($handlercontinue) {
        while(my $create = $_wxpst_createqueue->dequeue_nb) {
            my $result = { request => $create->{request} };
            #-----------------------------------------------------------------------
            # Child thread entry 
            #-----------------------------------------------------------------------
            my $modulename : shared = $create->{modulename};
            my $sendq : shared = $create->{sendq};
            my $recvq : shared = $create->{recvq};
            my $newthr = threads->create( \&_create_thread_from_handler, ($modulename, $sendq, $recvq));
            # ----------------------------------------------------------------------
            if(my $tid = $newthr->tid) {
                $result->{tid} = $tid;
            } else {
                $result->{tid} = 0;
            }
            $_wxpst_initqueue->enqueue($result);
        }
        sleep 0.20;
    }
    threads->exit;
}

sub _create_thread_from_handler {
    my($modulename, $sendq, $recvq) = @_;
    $modulename->require;
    $modulename->_set_wxpst_input_queue( $sendq );
    $modulename->_set_wxpst_output_queue( $recvq );
    $SIG{'KILL'} = sub { threads->exit(); };
    $SIG{'TERM'} = sub { $modulename->_set_wxpst_thread_continue(0); };
    $modulename->_wxpst_handle_input_queue;
}

sub _create_thread {
    my ( $modulename, $sendq, $recvq ) = @_;
    die qq(Cannot create threads outside the main thread) if threads->tid;
    $_threadrequests ++;
    my $reqno = $_threadrequests;
    my $proto = { modulename => $modulename, sendq => $sendq, recvq => $recvq , request => $reqno };
    
    $_wxpst_createqueue->enqueue($proto);
    
    my $newtid;
    while(1) {
        my $result = $_wxpst_initqueue->dequeue;
        
        if( $result->{request} == $reqno ) {
            $newtid = $result->{tid};
            last;
        } else {
            $_wxpst_initqueue->enqueue($result);
        }
    }
    
    return $newtid || undef;
}

END { $_wxpst_threadhandler->kill('TERM')->join; }

1;
